package com.wn.trade.service.impl;
import cn.hutool.core.util.RandomUtil;
import com.google.common.collect.Lists;

import java.math.BigDecimal;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wn.access.api.BankAccessApi;
import com.wn.common.exception.BusinessException;
import com.wn.common.model.dto.BorrowRecordDTO;
import com.wn.common.model.dto.bank.BankBidDTO;
import com.wn.common.model.dto.bank.BorrowRecordStatusBankDTO;
import com.wn.common.model.dto.pay.AgencyBidPayDTO;
import com.wn.common.model.dto.pay.BorrowRecordPayDTO;
import com.wn.common.model.dto.pay.PayBackDTO;
import com.wn.common.model.vo.TokenVo;
import com.wn.common.model.vo.UserInfoVo;
import com.wn.common.model.vo.borrow.BidBalanceDTO;
import com.wn.common.model.vo.borrow.BorrowRecordVo;
import com.wn.common.model.vo.borrow.LoanDetailVo;
import com.wn.common.model.vo.borrow.LoanVo;
import com.wn.common.result.BaseResult;
import com.wn.common.result.Result;
import com.wn.common.result.enums.impl.BusinessCode;
import com.wn.common.utils.CalculateExpectUtil;
import com.wn.common.utils.TokenUtil;
import com.wn.pay.api.BorrowPayPlanApi;
import com.wn.system.api.UserApi;
import com.wn.trade.bean.AgencyBid;
import com.wn.trade.bean.BorrowRecord;
import com.wn.trade.mapper.BorrowRecordMapper;
import com.wn.trade.service.IAgencyBidService;
import com.wn.trade.service.IBorrowRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author yellow Docter
 * @date 2023 -07-31
 * @desc    Primary可以理解为默认优先选择, 同时不可以同时设置多个,
 *          内部实质是设置BeanDefinition的primary属性
 */
@Slf4j
@Service
public class IBorrowRecordServiceImpl extends ServiceImpl<BorrowRecordMapper, BorrowRecord> implements IBorrowRecordService {
    @Autowired
    private UserApi userApi;

    @Autowired
    private BankAccessApi bankAccessApi;

    @Autowired
    private IAgencyBidService agencyBidService;

    @Autowired
    private BorrowPayPlanApi borrowPayPlanApi;

    /**
     * 申请借款    往  borrow_recored 表中添加数据
     * @param borrowRecordDTO
     */
    @Override
    public void borrow(BorrowRecordDTO borrowRecordDTO) {

        //拿到token 数据
        TokenVo tokenVo = TokenUtil.getTokenVo();

        //01 创建 借款 对象
        BorrowRecord borrowRecord = new BorrowRecord();

        //设置 借款的数据
        borrowRecord.setUserId(Long.parseLong(tokenVo.getUserId()));

        //随机生产一个编码
        borrowRecord.setBorrowRecordNo("BO" + UUID.randomUUID().toString().substring(0, 8));

        //借款类型
        borrowRecord.setType("xxx");

        // 借款时间
        borrowRecord.setPeriod(borrowRecordDTO.getPeriod());
        // 借款金额
        borrowRecord.setAmount(new BigDecimal(borrowRecordDTO.getAmount().toString()));

        //投资年化利率 (给投资者看的 )
        borrowRecord.setInvestRate(new BigDecimal("10"));

        //借款年化利率 利息
        borrowRecord.setBorrowerRate(new BigDecimal("10"));

        //平台佣金   500 --> 30
        borrowRecord.setCommissionRate(new BigDecimal("10"));

        //还款方式
        borrowRecord.setRepaymentWay("DEBX");

        //借款状态
        // 借款中、审批中 、到账
        borrowRecord.setBorrowStatus("借款中");

        //借款时间
        borrowRecord.setCreateDate(new Date());
        borrowRecord.setModifyDate(new Date());

        //借款 可用状态
        borrowRecord.setStatus(false);

        //借款流水号  (随机生成)
        borrowRecord.setReqCode("BO_REQ_CODE" + UUID.randomUUID().toString().substring(0, 8));

        //用户设置的信息
        // 通过远程调用拿到用户信息
        UserInfoVo userInfoVo = userApi.findUserById(tokenVo.getUserId());

        borrowRecord.setUserCode(userInfoVo.getUserCode());  //用户code
        borrowRecord.setName(userInfoVo.getRealName());      //真实姓名

        // 性别判断
        //拿到身份证的性别 做校验
        String sex = Integer.parseInt(userInfoVo.getIdentityCode()
                .substring(16, 17)) % 2 == 0 ? "女士" : "先生";

        //查询之前的借款次数
        long count = this.count(Wrappers.lambdaQuery(BorrowRecord.class).eq(
                BorrowRecord::getUserId, Long.parseLong(tokenVo.getUserId())
        ));

        String desc = userInfoVo.getRealName() + sex + ",第" + (count + 1) + "次借款...";
        //借款描述   xxx 先生 第1次借款
        borrowRecord.setDescription(desc);
        //02 保存
        this.save(borrowRecord);

    }

    /**
     * @param borrowId
     * @param status
     * @return 业务流程:
     * 1、trade微服务 拿到借款记录Id                  查询借款记录
     * 2、需要调用 托管微服    bank-access            (拿到借款记录)
     * bank-access 给（bank-system）银行系统发送数据      进行备份
     * 1、日志记录
     * 2、给银行系统发送数据
     * restTempdate 发送数据
     * <p>
     * 3、bank-system 返回 给bank-access
     * bank-access  返回数据 给  trade微服务
     * <p>
     * 4、 更新 借款状态
     */
    @Override
    public String changeStatus(Long borrowId, Integer status) {

        //01 查询  借款记录
        BorrowRecord borrowRecord = getById(borrowId);
        BorrowRecordVo borrowRecordVo = BeanUtil.copyProperties(borrowRecord, BorrowRecordVo.class);

        //02 远程调用  银行托管服务
        Result<BaseResult> result = bankAccessApi.syncBorrowRecord(borrowRecordVo);

        BaseResult baseResult = result.getData();
        //返回结果 进行 状态更新
        if (baseResult.getRespMsg().equals("成功")) {

          update(Wrappers.lambdaUpdate(BorrowRecord.class)
                    .set(BorrowRecord::getStatus, status)
                    .set(BorrowRecord::getBorrowStatus, "借款审批通过、等待放款")
                    .eq(BorrowRecord::getId, borrowId)
            );
        }

        return "success 借款审核通过...";
    }

    @Override
    public Map<String,Object> addAgencyBid(Long borrowId, Integer amount) {

        //01  机构查询账号余额
        TokenVo tokenVo = TokenUtil.getTokenVo();
        String userId = tokenVo.getUserId();
        UserInfoVo userInfoVo = userApi.findUserById(userId);
        String userCode = userInfoVo.getUserCode();

        //远程调用 查询银行
        // 银行金额详情
        BidBalanceDTO bidBalanceDTO = bankAccessApi.getBalanceDetails(userCode);

        log.info("查询用户的金额:{}",bidBalanceDTO);
        //    判断
        BigDecimal b = bidBalanceDTO.getBalance();
        // BigDecimal 转成double
        double v = b.doubleValue();
        if ( v < amount){
            log.info("余额不够不能购买 :{}",bidBalanceDTO);
            throw  new BusinessException(BusinessCode.BALANCE_NOT_ENOUGH);
        }

        //02  机构保存认购记录  ( 往 agency_bid表中添加数据 )
        AgencyBid agencyBid = new AgencyBid();
        //用户ID
        agencyBid.setUserId(Long.valueOf(tokenVo.getUserId()));
        agencyBid.setName(userInfoVo.getRealName());  //用户名
        agencyBid.setUserCode(userInfoVo.getUserCode());
        agencyBid.setAmount(new BigDecimal(amount));  //
        agencyBid.setBidStatus("0"); //投标状态
        agencyBid.setCreateDate(new Date());
        agencyBid.setReqCode(UUID.randomUUID().toString().substring(0,10));
        agencyBid.setStatus(0);

        BorrowRecord borrowRecord = this.getById(borrowId);

        agencyBid.setBorrowRecordId(borrowRecord.getId());
        agencyBid.setBorrowRecordNo(borrowRecord.getBorrowRecordNo());
        agencyBid.setBorrowRecordName(borrowRecord.getName());
        agencyBid.setBorrowRecordPeriod(borrowRecord.getPeriod());
        // 投资年化利率
        agencyBid.setInvestRate(borrowRecord.getInvestRate());

        agencyBidService.save(agencyBid);

        //03  调用托管服务 （如何指定的参数过去）
        BankBidDTO bankBidDTO = new BankBidDTO();

        // 投资金额
        bankBidDTO.setAmount(new BigDecimal(amount));
        //流水号  和 投资对象流水保持一致
        bankBidDTO.setReqCode(agencyBid.getReqCode());
        bankBidDTO.setUserCode(userInfoVo.getUserCode());
        bankBidDTO.setBizType("BID");
        //借款编号
        bankBidDTO.setBorrowRecordNo(borrowRecord.getBorrowRecordNo());

        log.info("交易平台访问托管服务平台的数据:{}",bankBidDTO);
        Result<BaseResult> baseResultResult = bankAccessApi.saveAgencyBid(bankBidDTO);
        // 发送  bankBidDTO 到 托管服务
        // 托管服务  发送  bankBidDTO 到银行系统
        //       1、保存日志
        //       2、调用银行系统
        //银行系统 返回数据
        //04   修改  agency_bid表中 的添加数据 进行同步
        BaseResult data = baseResultResult.getData();
        log.info("银行返回的数据==>:{}",data);
        if (data.getRespMsg().equals("成功")){

            //返回银行数据 进行修改
            agencyBidService.update(
                    Wrappers.lambdaUpdate(AgencyBid.class)
                            .set(AgencyBid::getBidStatus,1)
                            .set(AgencyBid::getStatus,1)
                            .eq(AgencyBid::getId,agencyBid.getId())
            );
        }

        //05  计算投资收益的结果
        BigDecimal money = CalculateExpectUtil.getIncomeTotalInterest(
                new BigDecimal(amount),//投资金额
                borrowRecord.getInvestRate(),//投资利率
                borrowRecord.getPeriod()//月份
        );

        Map<String,Object> map = new HashMap<>();

        map.put("money", "收益:" + money);
        map.put("amount","投资金额:" + amount);
        map.put("name","投资人:" + userInfoVo.getRealName());
        map.put("aid","投资产品的id:" + agencyBid.getId());

        return map;
    }

    /**
     * 借款审核
     * @param borrowId
     * @param commission
     * @param status
     * @return
     */
    @Override
    public String agencyBidStatus(Long borrowId, String commission, String status) {

        BorrowRecord borrowRecord = this.getById(borrowId);
        List<AgencyBid> agencyBidList  = agencyBidService.list(
                Wrappers.lambdaQuery(AgencyBid.class)
                        .eq(AgencyBid::getBorrowRecordId, borrowId)
        );
        //01  生成放款明细  （放款明细 把数据发送到 银行银行系统 进行 转账）
        //     组装 放款明细对象 data
        LoanVo loanVo = new LoanVo();

        loanVo.setId(RandomUtil.randomLong(300000000L));

        //借款详情 (一个借款 可以多个机构进行投资（本项目中只考虑一个） )
        LoanDetailVo loanDetailVo = new LoanDetailVo();
        // 借款金额
        loanDetailVo.setAmount(borrowRecord.getAmount());
        // 请求码  (给投资账号的流水号 银行系统通过这个号查询 金额进行转账 )
        loanDetailVo.setPreReqCode(agencyBidList.get(0).getReqCode());

        List<LoanDetailVo> loanDetailVos =new ArrayList<>();
        loanDetailVos.add(loanDetailVo);

        loanVo.setDetails(loanDetailVos);

        //平台的 佣金
        loanVo.setCommission(new BigDecimal(commission));
        loanVo.setBorrowRecordNo(borrowRecord.getBorrowRecordNo());
        // 放款明细的流水号 (借款的流水对应)
        loanVo.setReqCode(borrowRecord.getReqCode());

        //02  确认放款 （李总的冻结金额 转到 张三账户上） data
        Result<BaseResult> baseResultResult = bankAccessApi.confirmLoan(loanVo);
        log.info("拿到银行数据:{}",baseResultResult);

        //03  更改借款项目状态
        //银行 系统 (  sddfp_system_bank 下面的  borrow_record 表下面的状态 )
        //  修改 银行系统 借款的状态
        BorrowRecordStatusBankDTO borrowRecordStatusBankDTO = new BorrowRecordStatusBankDTO();
        // 流水号
        borrowRecordStatusBankDTO.setReqCode(UUID.randomUUID().toString().substring(0,10));
        // 借款id
        borrowRecordStatusBankDTO.setBorrowRecordNo(borrowRecord.getBorrowRecordNo());
        borrowRecordStatusBankDTO.setBorrowStatus("以放款");
        borrowRecordStatusBankDTO.setBorrowRecordId(borrowId);

        Result result = bankAccessApi.changeBorrowStatus(borrowRecordStatusBankDTO);

        if (result.getCode() == 200){

            //返回银行数据 进行修改  借款状态
            update(
                    Wrappers.lambdaUpdate(BorrowRecord.class)
                            .set(BorrowRecord::getBorrowStatus,"以放款")
                            .set(BorrowRecord::getStatus,1)     //数据是否同步 (和银行系统一致)
                            .eq(BorrowRecord::getId,borrowId)   //修改条件
            );
        }

        //交易 系统 (  sddfp_trade       下面的  borrow_record 表下面的状态 )

        //4. 交易中心向还款微服务发送请求，生成还款计划 [接口4] 生成还款计划
        //准备数据
        PayBackDTO payBackDTO=new PayBackDTO();
        //准备数据-借款记录
        BorrowRecordPayDTO borrowRecordPayDTO =
                BeanUtil.copyProperties(borrowRecord, BorrowRecordPayDTO.class);
        payBackDTO.setBorrowRecordPayDTO(borrowRecordPayDTO);
        //准备数据-机构投资申购信息
        List<AgencyBidPayDTO> agencyBidPayDTOS =
                BeanUtil.copyToList(agencyBidList, AgencyBidPayDTO.class);
        payBackDTO.setAgencyBids(agencyBidPayDTOS);
        //准备数据-投资人让利
        payBackDTO.setInvestorAnnualRate(new BigDecimal("0.02"));
        //准备数据-借款人让利
        payBackDTO.setBorrowerAnnualRate(new BigDecimal("0.03"));
        log.info("交易中心，还款计划数据：{}",payBackDTO);

        Result<String> resultPayBack = borrowPayPlanApi.payPlan(payBackDTO);
        log.info("还款计划生成成功!");

        return "success";
    }
}
